001 /* 002 * Copyright 2006 Stephen McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package net.dpml.http; 017 018 import java.io.InputStream; 019 import java.net.URI; 020 import java.net.URL; 021 import java.security.SecureRandom; 022 import java.security.KeyStore; 023 import java.security.NoSuchAlgorithmException; 024 import java.security.NoSuchProviderException; 025 026 import javax.net.ssl.KeyManager; 027 import javax.net.ssl.KeyManagerFactory; 028 import javax.net.ssl.TrustManager; 029 import javax.net.ssl.TrustManagerFactory; 030 import javax.net.ssl.SSLContext; 031 import javax.net.ssl.SSLServerSocketFactory; 032 033 import net.dpml.transit.Artifact; 034 035 import org.mortbay.resource.Resource; 036 import org.mortbay.jetty.security.Password; 037 038 /** 039 * SSL socket connector. 040 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 041 * @version 0.0.3 042 */ 043 public class SslSocketConnector extends org.mortbay.jetty.security.SslSocketConnector 044 { 045 private static final int HEADER_BUFFER_SIZE = 4*1024; 046 private static final int REQUEST_BUFFER_SIZE = 8*1024; 047 private static final int RESPONSE_BUFFER_SIZE = 32*1024; 048 private static final int MAXIMUM_IDLE_TIME = 30000; 049 private static final int ACCEPT_QUEUE_SIZE = 0; 050 private static final int ACCEPTORS = 1; 051 private static final int SO_LINGER_TIME = 1000; 052 private static final int CONFIDENTIAL_PORT = 0; 053 private static final int INTEGRAL_PORT = 0; 054 private static final boolean ASSUME_SHORT_DISPATCH = false; 055 private static final String KEYSTORE_TYPE = "JKS"; 056 private static final String PROTOCOL = "TLS"; 057 private static final String ALGORITHM = "SunX509"; 058 059 private transient Context m_context; 060 private transient Password m_certificatePassword; 061 private transient Password m_keystorePassword; 062 private transient Password m_trustPassword; 063 064 /** 065 * SSL connector context definition. 066 */ 067 public interface Context extends ConnectorContext 068 { 069 /** 070 * Set the cipher suites. 071 * @param suites the default suites argument 072 * @return the cipher suites 073 */ 074 //String[] getCipherSuites( String[] suites ); 075 076 /** 077 * Return the keystore password. 078 * @param password implementation defined default value 079 * @return the supplied value unless overriden in the deployment configuration 080 */ 081 String getKeyStorePassword( String password ); 082 083 /** 084 * Return the certificate password. 085 * @param password implementation defined default value 086 * @return the supplied value unless overriden in the deployment configuration 087 */ 088 String getCertificatePassword( String password ); 089 090 /** 091 * Return the keystore algorithm. 092 * @param algorithm implementation defined default value 093 * @return the supplied value unless overriden in the deployment configuration 094 */ 095 String getSecureRandomAlgorithm( String algorithm ); 096 097 /** 098 * Return the keystore type. 099 * @param type implementation defined default value 100 * @return the supplied value unless overriden in the deployment configuration 101 */ 102 String getKeyStoreType( String type ); 103 104 /** 105 * Return the SSL protocol. 106 * @param protocol implementation defined default value 107 * @return the supplied value unless overriden in the deployment configuration 108 */ 109 String getProtocol( String protocol ); 110 111 /** 112 * Return the keystore location uri. 113 * @param keystore implementation defined default value 114 * @return the supplied value unless overriden in the deployment configuration 115 */ 116 URI getKeyStore( URI keystore ); 117 118 /** 119 * Return the 'want-client-authentication' policy. 120 * @param flag implementation defined default value 121 * @return the supplied value unless overriden in the deployment configuration 122 */ 123 boolean getWantClientAuth( boolean flag ); 124 125 /** 126 * Return the 'need-client-authentication' policy. 127 * @param flag implementation defined default value 128 * @return the supplied value unless overriden in the deployment configuration 129 */ 130 boolean getNeedClientAuth( boolean flag ); 131 132 /** 133 * Return the SSL context provider. 134 * @param provider implementation defined default value 135 * @return the supplied value unless overriden in the deployment configuration 136 */ 137 String getProvider( String provider ); 138 139 // extras 140 141 /** 142 * Return the keystore algorithm. 143 * @param algorithm implementation defined default value 144 * @return the supplied value unless overriden in the deployment configuration 145 */ 146 String getTrustAlgorithm( String algorithm ); 147 148 /** 149 * Return the keystore location uri. 150 * @param uri implementation defined default value 151 * @return the supplied value unless overriden in the deployment configuration 152 */ 153 URI getTrustStore( URI uri ); 154 155 /** 156 * Return the keystore type. 157 * @param type implementation defined default value 158 * @return the supplied value unless overriden in the deployment configuration 159 */ 160 String getTrustStoreType( String type ); 161 162 /** 163 * Return the trust store password. 164 * @param password implementation defined default value 165 * @return the supplied value unless overriden in the deployment configuration 166 */ 167 String getTrustStorePassword( String password ); 168 } 169 170 /** 171 * Creation of a new ssl connector. 172 * @param context the deployment context 173 * @exception Exception if an instantiation error occurs 174 */ 175 public SslSocketConnector( Context context ) throws Exception 176 { 177 super(); 178 179 m_context = context; 180 181 String host = context.getHost( null ); 182 if( null != host ) 183 { 184 setHost( host ); 185 } 186 187 int port = context.getPort(); 188 setPort( port ); 189 190 int headerBufferSize = context.getHeaderBufferSize( HEADER_BUFFER_SIZE ); 191 setHeaderBufferSize( headerBufferSize ); 192 193 int requestBufferSize = context.getRequestBufferSize( REQUEST_BUFFER_SIZE ); 194 setRequestBufferSize( requestBufferSize ); 195 196 int responseBufferSize = context.getResponseBufferSize( RESPONSE_BUFFER_SIZE ); 197 setResponseBufferSize( responseBufferSize ); 198 199 int maxIdle = context.getMaxIdleTime( MAXIMUM_IDLE_TIME ); 200 setMaxIdleTime( maxIdle ); 201 202 int queueSize = context.getAcceptQueueSize( ACCEPT_QUEUE_SIZE ); 203 setAcceptQueueSize( queueSize ); 204 205 int acceptCount = context.getAcceptors( ACCEPTORS ); 206 setAcceptors( acceptCount ); 207 208 int linger = context.getSoLingerTime( SO_LINGER_TIME ); 209 setSoLingerTime( linger ); 210 211 int confidentialPort = context.getConfidentialPort( CONFIDENTIAL_PORT ); 212 setConfidentialPort( confidentialPort ); 213 214 Scheme confidentialScheme = Scheme.parse( context.getConfidentialScheme( "https" ) ); 215 setConfidentialScheme( confidentialScheme.getName() ); 216 217 int integralPort = context.getIntegralPort( INTEGRAL_PORT ); 218 setIntegralPort( integralPort ); 219 220 Scheme integralScheme = Scheme.parse( context.getIntegralScheme( "https" ) ); 221 setIntegralScheme( integralScheme.getName() ); 222 223 // SslSocketConnector$Context 224 225 String certificatePassword = context.getCertificatePassword( null ); 226 if( null != certificatePassword ) 227 { 228 m_certificatePassword = 229 Password.getPassword( KEYPASSWORD_PROPERTY, certificatePassword, null ); 230 setKeyPassword( certificatePassword ); 231 } 232 233 String keystorePassword = context.getKeyStorePassword( null ); 234 if( null != keystorePassword ) 235 { 236 m_keystorePassword = Password.getPassword( PASSWORD_PROPERTY, keystorePassword, null ); 237 setPassword( keystorePassword ); 238 } 239 240 String algorithm = context.getSecureRandomAlgorithm( ALGORITHM ); 241 setSecureRandomAlgorithm( algorithm ); 242 243 String protocol = context.getProtocol( PROTOCOL ); 244 setProtocol( protocol ); 245 246 URI keystore = context.getKeyStore( null ); 247 if( null != keystore ) 248 { 249 String keystorePath = keystore.toASCIIString(); 250 setKeystore( keystorePath ); 251 } 252 253 String provider = context.getProvider( null ); 254 if( null != provider ) 255 { 256 setProvider( provider ); 257 } 258 259 String keystoreType = context.getKeyStoreType( KEYSTORE_TYPE ); 260 setKeystoreType( keystoreType ); 261 262 boolean wantClientAuth = context.getWantClientAuth( false ); 263 setWantClientAuth( wantClientAuth ); 264 265 boolean needClientAuth = context.getNeedClientAuth( false ); 266 setNeedClientAuth( needClientAuth ); 267 268 //String[] suites = context.getCipherSuites( (String[]) null ); 269 //if( null != suites ) 270 //{ 271 // setCipherSuites( suites ); 272 //} 273 } 274 275 /** 276 * Create a new SSLServerSocketFactory. 277 * @return the factory 278 * @exception Exception if an error occurs during factory creation 279 */ 280 protected SSLServerSocketFactory createFactory() 281 throws Exception 282 { 283 final SSLContext context = getSSLContext(); 284 KeyManager[] keyManagers = getKeyManagers(); 285 TrustManager[] trustManagers = getTrustManagers(); 286 SecureRandom random = new SecureRandom(); 287 context.init( keyManagers, trustManagers, random ); 288 return context.getServerSocketFactory(); 289 } 290 291 private KeyManager[] getKeyManagers() throws Exception 292 { 293 final String algorithm = getSecureRandomAlgorithm(); 294 final KeyManagerFactory factory = KeyManagerFactory.getInstance( algorithm ); 295 final KeyStore store = loadKeyStore(); 296 final char[] password = toCharArray( m_certificatePassword ); 297 factory.init( store, password ); 298 return factory.getKeyManagers(); 299 } 300 301 private KeyStore loadKeyStore() throws Exception 302 { 303 final String type = getKeystoreType(); 304 final KeyStore keyStore = KeyStore.getInstance( type ); 305 final char[] password = toCharArray( m_keystorePassword ); 306 final String keyStorePath = getKeystore(); 307 final InputStream input = Resource.newResource( keyStorePath ).getInputStream(); 308 keyStore.load( input, password ); 309 return keyStore; 310 } 311 312 private KeyStore loadTrustStore() throws Exception 313 { 314 final String type = m_context.getTrustStoreType( KEYSTORE_TYPE ); 315 final KeyStore store = KeyStore.getInstance( type ); 316 final char[] password = toCharArray( m_trustPassword ); 317 URI uri = m_context.getTrustStore( null ); 318 if( null != uri ) 319 { 320 URL url = Artifact.toURL( uri ); 321 final InputStream input = Resource.newResource( url ).getInputStream(); 322 store.load( input, password ); 323 return store; 324 } 325 else 326 { 327 return null; 328 } 329 } 330 331 private TrustManager[] getTrustManagers() throws Exception 332 { 333 final String algorithm = m_context.getTrustAlgorithm( ALGORITHM ); 334 final TrustManagerFactory factory = TrustManagerFactory.getInstance( algorithm ); 335 final KeyStore store = loadTrustStore(); 336 if( store != null ) 337 { 338 factory.init( store ); 339 return factory.getTrustManagers(); 340 } 341 else 342 { 343 return new TrustManager[0]; 344 } 345 } 346 347 private char[] toCharArray( Password value ) 348 { 349 if( null == value ) 350 { 351 return null; 352 } 353 else 354 { 355 return value.toString().toCharArray(); 356 } 357 } 358 359 private SSLContext getSSLContext() throws NoSuchAlgorithmException, NoSuchProviderException 360 { 361 final String protocol = getProtocol(); 362 final String provider = getProvider(); 363 if( null == provider ) 364 { 365 return SSLContext.getInstance( protocol ); 366 } 367 else 368 { 369 return SSLContext.getInstance( protocol, provider ); 370 } 371 } 372 } 373